package cn.phantom.juctest;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.StampedLock;

@Slf4j(topic = "c.TestReadWriteLock")
public class TestReadWriteLock {
    public static void main(String[] args) throws InterruptedException {
        // DataContainer container = new DataContainer();
        DataContainerStamped dataContainerStamped = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainerStamped.read(1);
        }, "t1").start();

        // TimeUnit.SECONDS.sleep(0);

        new Thread(() -> {
            dataContainerStamped.read(1);
        }, "t2").start();
    }
}

@Slf4j(topic = "c.DataContainer")
class DataContainer {
    private Object data;
    private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
    private ReentrantReadWriteLock.ReadLock r = rw.readLock();
    private ReentrantReadWriteLock.WriteLock w = rw.writeLock();

    public Object read() {
        r.lock();
        try {
            log.debug("read...");
            return data;
        } finally {
            r.unlock();
        }
    }

    public void write() {
        w.lock();
        try {
            log.debug("write...");
        } finally {
            w.unlock();
        }
    }

}

@Slf4j(topic = "c.DataContainerStamped")
class DataContainerStamped {

    private int data;

    private final StampedLock lock = new StampedLock();

    public DataContainerStamped(int data) {
        this.data = data;
    }

    public int read(int readTime) {
        long stamp = lock.tryOptimisticRead();
        log.debug("optimistic read locking...{}", stamp);

        try {
            TimeUnit.SECONDS.sleep(readTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (lock.validate(stamp)) {
            log.debug("read finish...{}", stamp);
            return data;
        }

        // 锁升级--读锁
        log.debug("updating to read lock...{}", stamp);

        try {
            stamp = lock.readLock();
            log.debug("read lock {}", stamp);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("read finish...{}", stamp);
            return data;
        } finally {
            log.debug("read unlock... {}", stamp);
            lock.unlockRead(stamp);
        }


    }

    public void write(int newData) {
        long stamp = lock.writeLock();
        log.debug("write lock: {}", stamp);
        try {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.data = newData;
        } finally {
            log.debug("write unlock {}", stamp);
            lock.unlockWrite(stamp);
        }
    }

}